package jfprincesstests.fakes;

import java.util.Arrays;
import java.util.List;

import jfuzzyprincepless.zarchive.agents.BaseUtilityFuncAgent;
import jfuzzyprincepless.zarchive.algorithm.structure.ICommandConstruction;
import jfuzzyprincepless.zarchive.negotiation.services.IExecuteForService;
import jfuzzyprincepless.zarchive.negotiation.services.IUtilityInfoService;
import jfuzzyprincepless.negotiation.IExecutionContext;
import jfuzzyprincepless.negotiation.INegotiationContext;

public class FakeUtilityAgent
	extends BaseUtilityFuncAgent
	implements ICommandConstruction
{
	private StringBuilder sb;
	private String name;
	private Object[] criticals;
	private double[] utilities;

	private int criticalsPos = 0;
	private int utilitiesPos = 0;

	private boolean ignoreUtilServiceNotFound;

	private Object firstForElem = null;

	public FakeUtilityAgent(
		StringBuilder sb,
		String name,
		Object[] criticals,
		double[] utilities,
		boolean ignoreUtilServiceNotFound
	) {
		this.sb = sb;
		this.name = name;
		this.criticals = criticals;
		this.utilities = utilities;
		this.ignoreUtilServiceNotFound = ignoreUtilServiceNotFound;
	}

	public FakeUtilityAgent(
		StringBuilder sb,
		String name,
		Object[] criticals,
		double[] utilities
	) {
		this(sb, name, criticals, utilities, false);
	}

	@Override
	public boolean negotiate(INegotiationContext context) {
		appendIAmCritical(context);
		IExecuteForService<?> forServ =
			context.getService(IExecuteForService.class);
		if (forServ != null) {
			negotiateFor(forServ);
		}
		return super.negotiate(context);
	}

	@Override
	public void execute(IExecutionContext context) {
	}

	@Override
	public void onSingleNegotiationStageEnd(
		INegotiationContext context
	) {
		sb.append(name).append(",stgend");
	}

	@Override
	public double computeUtility(INegotiationContext context) {
		if (criticals.length > 0) {
			double result = utilities[utilitiesPos];
			utilitiesPos++;
			utilitiesPos %= utilities.length;
			return result;
		} else {
			return Double.MAX_VALUE;
		}
	}

	@Override
	public void addCriticalParams(List<Object> criticalParams) {
		if (criticals.length > 0) {
			criticalParams.add(criticals[criticalsPos]);
			criticalsPos++;
			criticalsPos %= criticals.length;
		}
	}

	private void negotiateFor(IExecuteForService<?> forServ) {
		sb.append(",for").append(forServ.getCurrentElem());
	}

	private void appendIAmCritical(INegotiationContext context) {
		IExecuteForService<?> forServ =
			context.getService(IExecuteForService.class);
		if (forServ != null) {
			if (firstForElem == null) {
				firstForElem = forServ.getCurrentElem();
			}
			if (forServ.getCurrentElem() != firstForElem) {
				return;
			}
		}
		IUtilityInfoService utilServ =
			context.getService(IUtilityInfoService.class);
		if (utilServ == null) {
			if (ignoreUtilServiceNotFound) {
				return;
			} else {
				throw new IllegalStateException(
					"IUtilityInfoService not found"
				);
			}
		}
		Object[] critParamArray = utilServ.getCriticalParams().toArray();
		sb.append(name).append(Arrays.toString(critParamArray));
		if (utilServ.getCriticalParams().size() > 0) {
			Object firstCritical = utilServ.getCriticalParams().get(0);
			if (Arrays.asList(criticals).indexOf(firstCritical) > -1) {
				sb.append("--i-am-critical");
			}
		}
	}
}
